package cn.hacynn.nd.service.impl;

import cn.hacynn.nd.common.constant.MessageConstant;
import cn.hacynn.nd.common.context.BaseContext;
import cn.hacynn.nd.common.enumeration.ShareStatus;
import cn.hacynn.nd.common.exception.FileException;
import cn.hacynn.nd.common.properties.ShareProperties;
import cn.hacynn.nd.common.utils.FileUtil;
import cn.hacynn.nd.common.utils.OSSUtil;
import cn.hacynn.nd.common.utils.RedisUtils;
import cn.hacynn.nd.mapper.*;
import cn.hacynn.nd.pojo.dto.CreateDirDTO;
import cn.hacynn.nd.pojo.dto.FileRenameDTO;
import cn.hacynn.nd.pojo.dto.FileUrlDTO;
import cn.hacynn.nd.pojo.dto.SaveFileDTO;
import cn.hacynn.nd.pojo.entity.*;
import cn.hacynn.nd.pojo.vo.DownloadListVO;
import cn.hacynn.nd.pojo.vo.FileDetailVO;
import cn.hacynn.nd.pojo.vo.FileSpaceVO;
import cn.hacynn.nd.service.FileService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author Hacynn
 * @Date 2024/5/25 17:47
 * @Version 1.0
 */
@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private OSSUtil ossUtil;
    @Autowired
    private SafeFileMapper safeFileMapper;
    @Autowired
    private TagFileMapper tagFileMapper;
    @Autowired
    private ShareMapper shareMapper;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ShareProperties shareProperties;

    /**
     * 获取当前用户文件列表信息
     * @return
     */
    @Override
    public List<File> list(String prefix) {
        // 获取用户id
        Long userId = BaseContext.getCurrentId();
        // 根据前缀进行筛选
        List<File> files = fileMapper.getByPrefix(prefix, userId);
        // 根据是否为目录进行排序（目录排在前面）
        sortByDir(files);
        // 设置前端显示格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd HH:mm");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        //更新时间
        files.forEach(f -> f.setLastModified(simpleDateFormat.format(f.getUpdateTime())));
        return files;
    }

    /**
     * 根据是否是文件夹进行排序，文件夹排在前面
     * @param list
     */
    @Override
    public void sortByDir(List<File> list) {
        if (list == null) {
            return;
        }
        list.sort(new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                int i1 = o1.getIsDir() ? 1 : 0;
                int i2 = o2.getIsDir() ? 1 : 0;
                return i2 - i1;
            }
        });
    }

    /**
     * 获取用户空间容量
     * @return
     */
    @Override
    public FileSpaceVO getFileSpace() {
        // 获得当前用户信息
        Long currentId = BaseContext.getCurrentId();
        User user = userMapper.selectById(currentId);
        // 计算已使用空间大小
        Long usedSpace = ossUtil.getUserUsedSpace(user.getBucketName());
        // 拼接前端显示字符串用户已经使用多少，用户总共多少
        String usedSizeStr = FileUtil.getFileSize(usedSpace);
        String totalSizeStr = FileUtil.getFileSize(user.getTotalSpace());
        FileSpaceVO fileSpaceVO = FileSpaceVO.builder()
                .used(usedSpace)
                .total(user.getTotalSpace())
                .spaceStr(usedSizeStr + "/" + totalSizeStr)
                .build();

        return fileSpaceVO;

    }

    /**
     * 新建文件夹
     * @param createDirDTO
     */
    @Override
    public void createDir(CreateDirDTO createDirDTO) {

        String prefix = createDirDTO.getPrefix();
        String dirName = createDirDTO.getDirName();

        // 获取当前用户
        Long currentId = BaseContext.getCurrentId();
        User user = userMapper.selectById(currentId);

        // 判断文件夹名称是否为空
        if (!StringUtils.hasLength(dirName)) {
            throw new FileException(MessageConstant.FOLDER_NAME_IS_EMPTY);
        }

        // 判断文件夹是否已存在并且判断文件夹是否在根目录还是不在根目录  TODO 可以简化写法，路径合并后直接查找oss_path和user_id即可（逻辑复杂）
        File file = null;
        if (StringUtils.hasLength(prefix)) {
            // 存在前缀路径
            String ossPath = prefix + dirName + "/";
            QueryWrapper<File> eq = new QueryWrapper<File>()
                    .eq("name", dirName)  // 可以不需要
                    .eq("oss_path", ossPath)
                    .eq("deleted", 0)  // 查出来的不能是已删除的
                    .eq("user_id", user.getId());
            file = fileMapper.selectOne(eq);
        } else {
            // 不存在前缀路径
            QueryWrapper<File> eq = new QueryWrapper<File>()
                    .eq("name", dirName)
                    .eq("parent_id", "-1")
                    .eq("type", "dir")    // 确保同名的是文件夹，如果同名的仅仅是文件则可以继续创建
                    .eq("deleted", 0)  // 查出来的不能是已删除的
                    .eq("user_id", user.getId());
            file = fileMapper.selectOne(eq);
        }
        if (Objects.nonNull(file)) {
            // 文件夹已存在
            throw new FileException(MessageConstant.FOLDER_EXISTS);
        }

        // 开始创建文件夹
        boolean created = ossUtil.createDir(user.getBucketName(), prefix, dirName);
        if (!created) {
            // 文件夹创建失败
            throw new FileException(MessageConstant.FOLDER_CREATE_FAILED);
        }

        // 向数据库插入数据   其是树的根节点
        String parent_id = "-1";
        if (StringUtils.hasLength(prefix)) {
            // 存在父目录  树的子节点
            parent_id = fileMapper.getIdByPrefix(prefix, user.getId());
        }
        File insertFile = File.builder()
                .name(dirName)
                .size(0L)
                .userId(user.getId())
                .type("dir")
                .isDir(true)
                .ossPath(prefix + dirName + "/")
                .parentId(parent_id)
                .build();
        int insert = fileMapper.insert(insertFile);
        if (insert == 0) {
            // 插入失败
            throw new FileException(MessageConstant.DATABASE_INSERT_FAILED);
        }
    }

    /**
     * 保存文件信息    将文件直接上传到线上，保存文件中的所有信息
     * synchronized 防止上传文件夹时同时查询不存在父目录而创建多个相同目录导致异常
     * @param saveFileDTO
     */
    @Override
    synchronized public void saveFile(SaveFileDTO saveFileDTO) {   // 加锁
        //查询用户信息
        Long currentId = BaseContext.getCurrentId();
        // 实体类转换
        File file = new File();
        //将其转化为DTO供数据库保存
        BeanUtils.copyProperties(saveFileDTO, file);
        file.setUserId(currentId);
        String ossPath = file.getOssPath();
        // 判断文件全路径名称中是否有目录（注意：不是判断是否为目录）
        Boolean hasDir = FileUtil.hasDir(ossPath);
        // 查询当前全路径名称的文件在数据库中是否已存在
        File fileDB = getFileByPath(file.getUserId(), ossPath);
        if (fileDB != null) {
            //这样在执行fileMapper.insert(file);时，会更新现有文件的信息而不是插入一个新的文件记录。
            // 如果文件已存在，设置文件ID  TODO 为什么要设置文件id，而不是直接结束？？防止文件覆盖???
            file.setId(fileDB.getId());
        }
        // 执行save的都是文件，非目录
        file.setIsDir(false);
        String parentId = "-1";

        // TODO 两个可以合并成一个
        if (hasDir) {
            // 以‘/’分割路径
            String[] separatePaths = FileUtil.separate(ossPath);
            // 创建根目录下的文件夹
            String tmpPath = separatePaths[0] + "/";
            File parentDir = fileMapper.selectOne(new QueryWrapper<File>()
                    .eq("oss_path", tmpPath)
                    .eq("user_id", file.getUserId()));
            if (parentDir == null) {
                // 数据库中不存在时，创建根目录
                parentDir = createParentDir(separatePaths[0], "-1", file.getUserId(), tmpPath);
            }
            // 更新父目录的id
            parentId = parentDir.getId();

            // 遍历路径上的各级别目录，逐级创建目录
            for (int i = 1; i < separatePaths.length - 1; i++) {   // 注意是 len - 1
                tmpPath += separatePaths[i] + "/";
                File currentDir = fileMapper.selectOne(new QueryWrapper<File>()
                        .eq("oss_path", tmpPath)
                        .eq("user_id", file.getUserId()));
                if (currentDir == null) {
                    // 路径文件夹不存在，创建
                    currentDir = createParentDir(separatePaths[i], parentId, file.getUserId(), tmpPath);
                }
                // 更新父目录的id
                parentId = currentDir.getId();
            }
        }
        // 获取文件类型（后缀）
        String type = cn.hutool.core.io.FileUtil.getSuffix(file.getName());
        file.setType(type);
        file.setParentId(parentId);
        // 更新数据库
        fileMapper.insert(file);
    }

    /**
     * true:将文件移入回收站
     * false:移出回收站，恢复文件
     * @param ossPath
     */
    @Override
    public void updateDeletedByOssPath(String ossPath, Boolean b) {
        if (ossPath == null) {
            // 文件路径为空
            throw new FileException(MessageConstant.FILE_PATH_IS_NULL);
        }
        // 获取用户id
        Long userId = BaseContext.getCurrentId();
        File file = fileMapper.selectOne(new QueryWrapper<File>()
                .eq("user_id", userId)
                .eq("oss_path", ossPath));
        if (file == null) {
            // 文件不存在
            throw new FileException(MessageConstant.FILE_NOT_EXIST);
        }
        // 修改删除状态
        file.setDeleted(b);
        fileMapper.updateById(file);   // 因为这里是全部更新，所以不会改变更新时间（新的时间被覆盖了）
    }

    /**
     * 获取回收站中的文件列表信息
     * @return
     */
    @Override
    public List<File> trashBinList() {
        //获取用户id
        Long userId = BaseContext.getCurrentId();
        return fileMapper.selectList(new QueryWrapper<File>()
                .eq("user_id", userId)
                .eq("deleted", true));
    }

    /**
     * 根据oss路径查询文件
     * @param userId
     * @param ossPath
     * @return
     */
    public File getFileByPath(Long userId, String ossPath) {
        if (ossPath == null) {
            return null;
        }
        return fileMapper.getFileByPath(userId, ossPath);
    }

    /**
     * 将回收站中的文件（或文件夹）彻底删除
     * @param filePath
     */
    @Override
    public void delete(String filePath) {
        if (filePath == null) {
            // 文件路径为空
            throw new FileException(MessageConstant.FILE_PATH_IS_NULL);
        }
        //查询用户id
        Long userId = BaseContext.getCurrentId();
        //查询文件信息
        File file = fileMapper.getFileByPath(userId, filePath);
        if (file == null) {
            // 文件为空
            throw new FileException(MessageConstant.FILE_NOT_EXIST);
        }
        // 从oss和数据库中删除该文件（文件夹）
        //查询用户信息
        User user = userMapper.selectById(userId);
        if (file.getIsDir()) {
            // 是文件夹
            deleteDir(user.getBucketName(), filePath);
        } else {
            // 是文件
            deleteFile(user.getBucketName(), filePath);
        }
    }

    /**
     * 清空回收站中的所有文件
     */
    @Override
    public void clearAllFiles() {
        //查询用户信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);
        //查询当前用户的状态deleted是true的文件
        List<File> files = fileMapper.selectList(new QueryWrapper<File>()
                .eq("user_id", userId)
                .eq("deleted", true));
        if (files == null || files.isEmpty()) {
            // 回收站为空
            throw new FileException(MessageConstant.TRASH_BIN_IS_EMPTY);
        }
        // 遍历集合，分别从oss和数据库中删除查询到的所有文件
        String bucketName = user.getBucketName();
        files.forEach(f -> {
            // 删除oss和数据库上的记录
            if (f.getIsDir()) {
                // 文件夹，删除文件夹的同时删除文件夹下的所有文件
                deleteDir(bucketName, f.getOssPath());
            } else {
                // 文件，仅删除文件本身
                deleteFile(bucketName, f.getOssPath());
            }
        });
    }

    /**
     * 文件重命名
     * @param fileRenameDTO
     */
    @Override
    public void rename(FileRenameDTO fileRenameDTO) {
        //获取文件姓名
        String newName = fileRenameDTO.getNewName();
        //获取文件路径
        String ossPath = fileRenameDTO.getOssPath();
        //获取文件id
        Long userId = BaseContext.getCurrentId();
        // 根据原路径查找文件
        File file = fileMapper.getFileByPath(userId, ossPath);
        if (file == null) {
            // 文件不存在
            throw new FileException(MessageConstant.FILE_NOT_EXIST);
        }
        if (file.getName().equals(newName)) {
            // 新名字和旧名字相同，直接返回
            return;
        }
        // 查询是否存在同名文件（文件夹）
        String tmpOssPath = ossPath;
        if (file.getIsDir()) {  // 如果要修改的是文件夹
            tmpOssPath = ossPath.substring(0, ossPath.length() - 1);  // 去掉文件夹路径后的“/”
        }
        //生成新路径
        String newOssPath = tmpOssPath.substring(0, tmpOssPath.lastIndexOf("/") + 1) + newName;
        if (file.getIsDir()) {  // 如果是文件夹，再加回去"/"，文件夹以"/"结尾
            newOssPath += "/";
        }
        File fileDB = fileMapper.getFileByPath(userId, newOssPath);   // 使用新路径查询是否存在同名文件（文件名）
        if (fileDB != null) {
            // 文件名重复
            throw new FileException(MessageConstant.FILE_NAME_DUPLICATION);
        }

        // 执行重命名操作
        //查询当前用户信息
        User user = userMapper.selectById(userId);
        // 文件
        if (!file.getIsDir()) {
            // 如果是分享的文件，更新分享文件的状态
            updateShareFileStatus(userId, file.getOssPath(), ShareStatus.DELETED.getCode());
            // 数据库修改
            file.setOssPath(newOssPath);
            file.setName(newName);
            file.setUpdateTime(new Date());
            fileMapper.updateById(file);
            // oss修改
            ossUtil.FileRename(user.getBucketName(), ossPath, newOssPath);
            return;
        }
        // 文件夹
        List<File> files = getFilesByDir(userId, file);  // 【BFS】结果包含了文件夹本身
        for (File f : files) {
            // 如果是分享的文件，更新分享文件的状态
            updateShareFileStatus(userId, f.getOssPath(), ShareStatus.DELETED.getCode());
            // 统一修改ossPath
            String oldFilePath = f.getOssPath();
            String suffix = oldFilePath.substring(ossPath.length());   // 后缀
            String newFilePath = newOssPath + suffix;
            f.setOssPath(newFilePath);
            fileMapper.updateById(f);
        }
        // 修改重命名的文件夹的名称以及更新时间
        file.setName(newName);
        file.setUpdateTime(new Date());
        fileMapper.updateById(file);
        // 修改oss中的文件夹名称
        ossUtil.DirRename(user.getBucketName(), ossPath, newOssPath);
    }

    /**
     * 查看文件详情
     * @param ossPath
     * @return
     */
    @Override
    public FileDetailVO detail(String ossPath) {
        //获取用户id
        Long userId = BaseContext.getCurrentId();
        //获取文件信息
        File file = fileMapper.getFileByPath(userId, ossPath);
        if (file == null) {
            // 文件不存在
            throw new FileException(MessageConstant.FILE_NOT_EXIST);
        }
        // 设置前端显示的格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 计算文件大小
        Long size = null;
        if (file.getIsDir()) {   // 文件夹
            size = calculateDirSize(file);
        } else {  // 文件
            size = file.getSize();
        }
        return FileDetailVO.builder()
                .name(file.getName())
                .size(size)
                .type(file.getIsDir() ? "文件夹" : file.getType())
                .createTimeStr(simpleDateFormat.format(file.getCreateTime()))
                .ossPath(file.getOssPath())
                .lastModified(simpleDateFormat.format(file.getUpdateTime()))
                .build();
    }

    /**
     * 获取预览地址
     * @param fileUrlDTO
     * @return
     */
    @Override
    public String previewUrl(FileUrlDTO fileUrlDTO) {
        // 获取文件路径
        String ossPath = fileUrlDTO.getOssPath();
        //获取过期时间
        Date date = new Date(fileUrlDTO.getExpireTime());
        //获取用户信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);
        //获取文件信息
        File file = fileMapper.getFileByPath(userId, ossPath);
        if (file == null) {
            // 文件不存在
            throw new FileException(MessageConstant.FILE_NOT_EXIST);
        }
        return ossUtil.generateUrl(user.getBucketName(), ossPath, date);
    }

    /**
     * 文件下载
     * @param ossPath
     * @return
     */
    @Override
    public String download(String ossPath) {
        //获取用户信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);
        // 指定生成的签名URL过期时间，单位为毫秒，此处设置为10分钟
        Date expiration = new Date(new Date().getTime() + 600 * 1000L);
        //生成一个临时的下载 URL，用于从 OSS（对象存储服务）中下载文件
        return ossUtil.generateUrl(user.getBucketName(), ossPath, expiration);
    }

    /**
     * 获取分享地址
     * @param fileUrlDTO
     * @return
     */
    @Override
    public String shareUrl(FileUrlDTO fileUrlDTO) {
        //后去文件路径
        String ossPath = fileUrlDTO.getOssPath();
        // 过期时间
        Date expireTime = new Date(fileUrlDTO.getExpireTime());
        //获取用户信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);
        //获取文件信息
        File file = fileMapper.getFileByPath(userId, ossPath);
        if (file == null) {
            // 文件不存在
            throw new FileException(MessageConstant.FILE_NOT_EXIST);
        }
        // 设置oss路径
        String url = file.getOssPath();  // 是文件夹，则暂存oss路径
        if (!file.getIsDir()) {  // 是文件，则直接存文件本身的下载路径
            url = ossUtil.generateUrl(user.getBucketName(), ossPath, expireTime);
        }
        // 计算文件大小
        Long size = file.getSize();  // 是文件
        if (file.getIsDir()) {  // 是文件夹
            size = calculateDirSize(file);
        }
        Share share = Share.builder()
                .fileName(file.getName())
                .fileType(file.getType())
                .userName(user.getName())
                .expireTime(expireTime)
                .url(url)   // 文件的下载链接、文件夹的oss路径
                .userId(userId)
                .size(size)
                .filePath(file.getOssPath())
                .status(ShareStatus.NORMAL.getCode())  // 分享有效
                .build();
        // 将记录插入数据库中
        shareMapper.insert(share);
        // 生成短链分享链接
        Long expireSeconds = (expireTime.getTime() - System.currentTimeMillis()) / 1000;   // 毫秒-->秒
        // 生成一个随机 UUID。截取5到16位
        String shortLink = UUID.randomUUID().toString().substring(5, 16);
        //存入redis中
        redisUtils.set(shortLink, share.getId(), expireSeconds);
        redisUtils.set(share.getId(), shortLink, expireSeconds);
        // 向redis中存入文件的下载链接、文件夹的oss路径
        redisUtils.set(shareProperties.getRedisSharePrefix() + share.getId(), url, expireSeconds);  // share:+FileId
        return shareProperties.getLinkPrefix() + shortLink;  // 前端分享地址 + 短链
    }

    /**
     * 文件夹下载（打包下载文件）
     * @param dirPath
     * @return
     */
    @Override
    public List<DownloadListVO> downloadDir(String dirPath) {
        //获取用户信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);
        //获取文件信息
        File file = fileMapper.getFileByPath(userId, dirPath);
        if (file == null) {
            // 文件夹不存在
            throw new FileException(MessageConstant.DIR_NOT_EXIST);
        }
        if (!file.getIsDir()) {
            // 不是文件夹
            throw new FileException(MessageConstant.NOT_FOLDER);
        }
        // 获得该文件夹的所有文件，包括自己
        List<File> files = getFilesByDir(userId, file);
        List<DownloadListVO> downloadListVOList = new ArrayList<>();
        files.forEach(f -> {
            if (!f.getIsDir()) {   // 不是文件夹时
                String foldPath = f.getOssPath().substring(0, f.getOssPath().lastIndexOf('/') + 1);  // 文件所属文件夹路径
                DownloadListVO build = DownloadListVO.builder()
                        .url(ossUtil.generateUrl(user.getBucketName(), f.getOssPath(), new Date(System.currentTimeMillis() + 1000 * 60 * 60)))
                        .fileName(f.getName())
                        .foldPath(foldPath)
                        .build();
                downloadListVOList.add(build);
            }
        });
        return downloadListVOList;
    }

    /**
     * 如果是分享的文件，更新分享文件的状态
     * @param userId
     * @param ossPath
     * @param status
     */
    private void updateShareFileStatus(Long userId, String ossPath, int status) {
        List<Share> shares = shareMapper.getSharingFilesByPath(userId, ossPath, status);  // 如果是分享的文件，还需要修改分享文件的状态
        if (!shares.isEmpty()) {
            // 分享中的文件
            for (Share share : shares) {
                share.setStatus(status);   // 修改分享文件状态
                shareMapper.updateById(share);
            }
        }
    }

    /**
     * 计算文件夹大小
     * @param file
     * @return
     */
    private Long calculateDirSize(File file) {
        Long userId = BaseContext.getCurrentId();
        List<File> files = getFilesByDir(userId, file);
        Long ret = 0L;
        if (files.size() == 1) {   // 只有根节点本身，即是一个空文件夹
            return ret;
        }
        // 过滤文件夹对象、回收站以及保险箱内的文件
        for (File f : files) {
            if (!f.getIsDir() && !f.getInSafe() && !f.getDeleted()) {  // 不是文件夹，不是保险箱内的文件，不是已删除的文件
                ret += f.getSize();
            }
        }
        return ret;
    }

    /**
     * 从回收站中删除文件
     * @param bucketName
     * @param ossPath
     */
    private void deleteFile(String bucketName, String ossPath) {
        // 从oss中删除
        ossUtil.deleteFile(bucketName, ossPath);
        // 从数据库中删除
        Long userId = BaseContext.getCurrentId();
        File file = fileMapper.getFileByPath(userId, ossPath);
        if (!file.getIsDir()) {
            // 从File表中彻底删除
            fileMapper.deleteById(file);
            // 删除受影响的记录
            if (file.getInSafe()) {   // 如果是保险箱中的文件，还需要删除SafeFile表中的记录
                safeFileMapper.delete(new QueryWrapper<SafeFile>().eq("file_id", file.getId()));
            }
            List<TagFile> tagFiles = tagFileMapper.selectList(new QueryWrapper<TagFile>().eq("file_id", file.getId()));
            if (!tagFiles.isEmpty()) {   // 如果文件存在标签，还需要删除TagFile表中的记录
                List<String> ids = tagFiles.stream().map(TagFile::getId).collect(Collectors.toList());
                tagFileMapper.deleteBatchIds(ids);
            }
            // 更新分享文件的状态，更新为删除
            updateShareFileStatus(userId, ossPath, ShareStatus.DELETED.getCode());
        }
    }

    /**
     * 从回收站中删除该文件夹，包括该文件夹下的所有文件
     * @param bucketName
     * @param ossPath
     */
    private void deleteDir(String bucketName, String ossPath) {
        //查询用户id
        Long userId = BaseContext.getCurrentId();
        // 查询出要删除的文件夹
        File Dir = fileMapper.getFileByPath(userId, ossPath);
        if (Dir == null) {
            // 文件夹不存在
            throw new FileException(MessageConstant.DIR_NOT_EXIST);
        }
        // 获取该文件夹下的所有子文件（孩子），包括子文件的文件，其中文件夹也属于孩子【BFS算法】
        List<File> files = getFilesByDir(userId, Dir);
        // 删除数据库上的文件
        for (File file : files) {
            // 如果是分享的文件，更新分享文件的状态
            updateShareFileStatus(userId, file.getOssPath(), ShareStatus.DELETED.getCode());
            // 删除受影响的记录
            if (file.getInSafe()) {
                // 如果是保险箱中的文件，还需要删除SafeFile表中的记录
                safeFileMapper.delete(new QueryWrapper<SafeFile>().eq("file_id", file.getId()));
            }
            List<TagFile> tagFiles = tagFileMapper.selectList(new QueryWrapper<TagFile>().eq("file_id", file.getId()));
            if (!tagFiles.isEmpty()) {
                // 如果文件存在标签，还需要删除TagFile表中的记录
                List<String> ids = tagFiles.stream().map(TagFile::getId).collect(Collectors.toList());
                tagFileMapper.deleteBatchIds(ids);
            }
        }
        List<String> ids = files.stream().map(File::getId).collect(Collectors.toList());  // 集合id，用于存放要删除的文件/文件夹id
        fileMapper.deleteBatchIds(ids);  // 批量操作删除数据库中的文件
        // 删除oss上的文件
        ossUtil.deleteDir(bucketName, ossPath);
    }

    /**
     * 使用【BFS算法】查找文件夹下所有文件（包括文件夹）的信息
     * @param userId
     * @param dir
     * @return
     */
    public List<File> getFilesByDir(Long userId ,File dir) {
        //创建File类型的List用来存储文件
        List<File> resFiles = new ArrayList<>();
        // 创建一个队列用于广度优先遍历
        Queue<File> queue = new LinkedList<>();
        // 添加父节点
        queue.add(dir);
        while (!queue.isEmpty()) {
            //从队列中取出一个文件或目录
            File pollFile = queue.poll();
            // 判断该文件是否是目录
            if (pollFile.getIsDir()) {
                // 获取该目录的子文件和子目录列表
                List<File> childFiles = fileMapper.getChildFilesById(userId, pollFile.getId());
                // 该节点存在孩子时
                if (childFiles != null) {
                    // 将所有子文件和子目录添加到队列中
                    queue.addAll(childFiles);
                }
            }
            // 将当前文件或目录添加到结果列表中
            resFiles.add(pollFile);
        }
        //返回所有的文件信息
        return resFiles;
    }

    /**
     * 当上传文件夹时，创建根目录
     * @param name
     * @param parentId
     * @param userId
     * @param tmpPath
     * @return
     */
    private File createParentDir(String name, String parentId, Long userId, String tmpPath) {
        File dir = getFileByPath(userId, tmpPath);
        File parentDir = File.builder()
                .name(name)
                .isDir(true)
                .parentId(parentId)
                .type("dir")
                .userId(userId)
                .ossPath(tmpPath)
                .size(0L)
                .build();
        if (dir != null) {
            // 如果已存在，则赋值原id
            parentDir.setId(dir.getId());
        }
        // 插入数据库
        fileMapper.insert(parentDir);
        return parentDir;
    }


}
